import { List } from '@knno/ui/list';
import { Cell, CellProps, SearchableItem } from '../../doc/cell';
import { PopList } from '@knno/ui/popList';
import { h } from '@knno/dom/jsx';
import { renderIf } from '../../utils';

type SelectCallback = (value: unknown, text: unknown, row: number) => void;

interface SelectHandler {
	list: List;
	close(): void;
	onSelect(callback: SelectCallback): this;
	onClose(callback: () => void): this;
}
const ASCII_LOWERCASE = Array.from('abcdefghijklmnopqrstuvwxyz');
const ASCII_UPPERCASE = ASCII_LOWERCASE.map((a) => a.toUpperCase());
const NUMBERS = Array.from('0123456789');
const KEYS = new Set<string>(
	['Backspace', 'Delete', 'ArrowDown', ' ', 'Space'].concat(NUMBERS, ASCII_UPPERCASE, ASCII_LOWERCASE)
);

export function searchable(cell: Cell, attrs: CellProps) {
	if (!attrs.readonly) {
		cell.attr('contenteditable', 'plaintext-only');
	}
	cell.text(cell.getValue()?.name ?? '');
	let pop: SelectHandler | undefined;
	let searchText = '';
	async function openSelect() {
		const keyword = cell.text();
		if (keyword.trim().length === 0) {
			pop?.close();
			return;
		}
		searchText = keyword;
		const options = attrs.searchFunc ? await attrs.searchFunc(keyword) : [];
		if (searchText !== keyword || (document.activeElement !== cell.elems()[0] && !pop)) {
			return;
		}
		if (options.length === 0) {
			pop?.close();
			return;
		}
		if (pop) {
			pop.list.rows(options);
			return;
		}
		pop = PopList.show(
			options,
			{
				text: 'text',
				value: 'value',
				formatter(row) {
					const r = row as SearchableItem;
					return (
						<div
							style={{
								flex: '1',
								display: 'flex',
								flexDirection: 'column',
								justifyContent: 'center',
								paddingRight: '1em',
							}}
						>
							<div style={{ fontWeight: 'bold', fontSize: '1.1em' }}>{r.text}</div>
							{renderIf(!!r.desc, () => (
								<div style={{ color: '#666' }}>{r.desc}</div>
							))}
						</div>
					);
				},
			},
			{
				refer: cell,
				activeValue: null,
				multiSelect: false,
				useAction: true,
				useSearch: false,
				popupPosition: 'bottom-right',
				focus: false,
				width: '30em',
			}
		)
			.onSelect((value, text) => {
				cell.setValue({ code: value, name: text + '' }, true);
				const t = cell.getValue()?.name ?? '';
				cell.text(t.trim());
				const sel = window.getSelection();
				if (sel) {
					sel.removeAllRanges();
					const range = document.createRange();
					range.selectNode(cell.elems()[0]);
					range.setEnd(cell.elems()[0], cell.elems()[0].childNodes.length);
					range.collapse(false);
					sel.addRange(range);
				}
				const userItem = options.find((item) => item.value === value);
				cell.emitChange(userItem);
			})
			.onClose(() => {
				pop = undefined;
			});
	}

	let openTimer: NodeJS.Timeout | null = null;
	cell.on('keyup', (evt) => {
		if (!KEYS.has(evt.key)) {
			return;
		}
		if (evt.key === 'Enter') {
			evt.preventDefault();
			return;
		}
		if (!attrs.readonly) {
			if (openTimer) clearTimeout(openTimer);
			openTimer = setTimeout(() => {
				openSelect();
			}, 100);
		}
	});
	cell.on('keydown', (evt) => {
		if (evt.key === 'Enter' && attrs.multiline !== true) {
			evt.preventDefault();
		} else if (evt.key === 'ArrowDown') {
			if (pop) {
				pop.list.focus();
				pop.list.active(0);
				evt.preventDefault();
			}
		}
	});
	cell.on('input', () => {
		if (!cell.text()) {
			cell.setValue(undefined);
		} else {
			cell.setValue({ code: cell.text(), name: cell.text() });
		}
		cell.emitChange();
	});
}
